using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Gko.Utils
{
	/// <summary>
	/// Base class for deriving from unmanaged interface
	/// </summary>
	public abstract class UnmanagedInterfaceImplementation : UnmanagedInterface
	{
		private UnmanagedMemoryBlock vTable;
		/// <summary>
		/// Initializes a new instance of the <see cref="T:Gko.Utils.UnmanagedInterfaceImplementation"/> class.
		/// </summary>
		protected UnmanagedInterfaceImplementation()
			: base(CreateVTable())
		{
			this.nativeVirtualFunctionsTable.Interface.AttachVTable(IntPtr.Zero);
		}

		private static IntPtr CreateVTable()
		{
			return Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeInterface)));
		}

		/// <summary>
		/// Adds the method.
		/// </summary>
		/// <typeparam name="TDelegate">The type of the delegate.</typeparam>
		/// <param name="method">The method.</param>
		protected void AddMethod<TDelegate>(TDelegate method) where TDelegate : class
		{
			this.InternalAddMethod(method);
		}

		/// <summary>
		/// Generates the virtual table.
		/// </summary>
		protected void GenerateVTable()
		{
			if (this.nativeVirtualFunctionsTable.Interface.VTable != IntPtr.Zero) {
				Marshal.FreeHGlobal(this.nativeVirtualFunctionsTable.Interface.VTable);
			}
			this.vTable = UnmanagedMemoryBlock.AllocateHGlobal(this.methods.Count * IntPtr.Size);
			this.AddUnmanagedDisposed(this.vTable);

			this.nativeVirtualFunctionsTable.Interface.AttachVTable(this.vTable);
			var index = 0;
			foreach (var method in this.methods.Values) {
				this.nativeVirtualFunctionsTable.SetMethod((Delegate)method, index++);
			}
		}

		protected override void OnDisposeUnmanaged()
		{
			base.OnDisposeUnmanaged();
			Marshal.FreeHGlobal(this.UnsafeHandle);
		}
	}
}